/*  audioscrobbler.c
 *
 *  Copyright (C) 2008 Guillaume Béland <guillaume.beland@gmail.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
 *
 *  Author : Guillaume Béland <guillaume.beland@gmail.com>
*/ 

#include <xmmsclient/xmmsclient.h>
#include <xmmsclient/xmmsclient-glib.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <glib/gprintf.h>
#include <curl/curl.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "md5.h"
#include "audioscrobbler.h"

/* Utility function declaration */
static gchar*   makemd5 (char *string);
static gchar*   ifnull (char *s);
static gboolean song_is_submitable (gint length);
static gint     calculate_submit_time (xmms_audioscrobbler_song_t *song);
static gchar*   lastfm_parse (gchar *s);

/* Globals */
static guint last_duration = 0;
static guint playback_status = 0;
static guint current_id;
static guint sleep_time;

/**
 * Handle the playback status
 */
void
xmms_audioscrobbler_handle_playback_status (xmmsc_result_t *result,  void *userdata) {
   
        guint status;
	xmms_client_data_t *clientdata = userdata;
        /* 0 = stopped, 1 = playing, 2= paused */
        if (!xmmsc_result_get_uint (result, &status)) {
	        printf ("\n[ERROR] Broken resultset");
        }

        playback_status = status; 
                
        /* if the playback stop and the queue isn't empty, submit remaining entry*/
        if (last_duration > sleep_time && !g_queue_is_empty (clientdata->audioscrobbler->queue))
                xmms_audioscrobbler_submit_data (clientdata->audioscrobbler, 0);
        
}

/**
 * Set last_duration to the current playtime.
 */       
void
xmms_audioscrobbler_handle_playtime (xmmsc_result_t *res, G_GNUC_UNUSED void *userdata) {

	guint dur;
        xmmsc_result_t *newres;
        
	if (xmmsc_result_iserror (res)) {
		printf ("\n[ERROR] %s", xmmsc_result_get_error (res));
	}

	if (!xmmsc_result_get_uint (res, &dur)) {
		printf ("\n[ERROR] Broken resultset");
	}

	last_duration = dur;

        newres = xmmsc_result_restart (res);
	xmmsc_result_unref (res);
        xmmsc_result_unref (newres);
}

/**
 * Create an entry and make a call to xmms_audioscrobbler_add_to_queue () to 
 * current song to the submission queue
 */
void
xmms_audioscrobbler_handle_current_id (xmmsc_result_t *result, G_GNUC_UNUSED void *cdata) {
        
        unsigned int id;

        xmms_client_data_t *clientdata = (xmms_client_data_t *) cdata;

        if (!xmmsc_result_get_uint (result, &id)) {
		fprintf (stderr, "\n[ERROR] Result didn't contain right type!\n");
	}

        if (current_id != id && last_duration > sleep_time)
                xmms_audioscrobbler_submit_data (clientdata->audioscrobbler, 0);

        current_id = id;
        
	result = xmmsc_medialib_get_info (clientdata->connection, current_id);
	xmmsc_result_notifier_set (result, xmms_audioscrobbler_do_mediainfo, clientdata);
	xmmsc_result_unref (result);

}

void
xmms_audioscrobbler_do_mediainfo (xmmsc_result_t *medialib_result, G_GNUC_UNUSED void *userdata) {

        xmms_client_data_t *clientdata = (xmms_client_data_t *) userdata;
	const char *val;
	int intval;
        time_t timestamp;
        
        xmms_audioscrobbler_song_t *entry;
        entry = g_new (xmms_audioscrobbler_song_t,1);

	if (xmmsc_result_iserror (medialib_result)) {
		fprintf (stderr, "\nMedialib get info returns error, %s\n",
		         xmmsc_result_get_error (medialib_result));
		return;
	}
        /* get some metadata...*/
	if (!xmmsc_result_get_dict_entry_string (medialib_result, "artist", &val)) {
		val = "No artist";
	}
        entry->artist = g_strdup (val);

	if (!xmmsc_result_get_dict_entry_string (medialib_result, "title", &val)) {
		val = "No Title";
	}
        entry->track = g_strdup (val);

	if (!xmmsc_result_get_dict_entry_string (medialib_result, "album", &val)) {
		val = "\nAlbum";
	}
        entry->album = g_strdup (val); 

	if (!xmmsc_result_get_dict_entry_int (medialib_result, "duration", &intval)) {
		intval = 0;
	}
        entry->length = intval / 1000;

        if (!xmmsc_result_get_dict_entry_string (medialib_result, "track_id", &val)) {
                val = "";
        }
        entry->mbtrackid = g_strdup (val);

        if (!xmmsc_result_get_dict_entry_int (medialib_result, "tracknr", &intval)) {
                intval = 0;
        }
        entry->tracknumber = intval;

        entry->started_playing = time (NULL); 
        entry->source = 'P';        
        entry->rating = 'L';
        
        time (&timestamp);
        snprintf (entry->timestamp, 33, "%llu", (uintmax_t)timestamp );
       
        xmms_audioscrobbler_queue_for_submit (clientdata, entry);
        
        xmmsc_result_unref (medialib_result);
}

void 
xmms_audioscrobbler_queue_for_submit ( xmms_client_data_t *clientdata, xmms_audioscrobbler_song_t *entry) {

        /* add it to the queue and submit the now playing track */
        if (song_is_submitable (entry->length)) {
                if (xmms_audioscrobbler_add_to_queue (clientdata->audioscrobbler, entry)) {
                        printf ("\n[INFO] Succesfully added %s - %s to submit queue", entry->artist, entry->track);
                        xmms_audioscrobbler_submit_data (clientdata->audioscrobbler, 1);
                        sleep_time = calculate_submit_time (entry); 
                } else {
                        printf ("\n[INFO] Failed to add %s - %s to submit queue", entry->artist, entry->track);
                }
        } else {
                printf ("\n[INFO] %s - %s is too short to be submitted", entry->artist, entry->track);
                xmms_audioscrobbler_song_free (entry);
        }
}

/**
 * add a xmms_audioscrobbler_song_t to a GQueue
 * returns TRUE if the song was added succesfully 
 */
gboolean
xmms_audioscrobbler_add_to_queue (xmms_audioscrobbler_data_t *audioscrobbler,
                                        xmms_audioscrobbler_song_t *entry) {

	g_queue_push_tail (audioscrobbler->queue, entry);
	audioscrobbler->queue_changed = TRUE;
	audioscrobbler->queue_count++;   
	return TRUE;
}

/**
 * Create, initialize and return a ready to use audioscrobbler object
 * returns A xmms_audioscrobbler_data_t initialized properly
 */
xmms_audioscrobbler_data_t*
xmms_audioscrobbler_init (void) {

        xmms_audioscrobbler_data_t *audioscrobbler;

        g_printf ("\n[INFO] Initialising Audioscrobbler...");

        audioscrobbler = g_new (xmms_audioscrobbler_data_t, 1);
        audioscrobbler->session_id = g_strdup ("");
        audioscrobbler->username = g_strdup ("");
        audioscrobbler->password = g_strdup ("");
        audioscrobbler->submit_url = g_strdup ("");
        audioscrobbler->now_playing_url = g_strdup ("");
        audioscrobbler->srv_response = g_string_new (NULL);
        audioscrobbler->queue_count = 0;
        audioscrobbler->handshake = FALSE;
        audioscrobbler->debug = FALSE;
       
        audioscrobbler->queue = g_queue_new ();
        audioscrobbler->submission = g_queue_new ();
        
        g_printf ("\n[INFO] Audioscrobbler Initialized...");
        
        return audioscrobbler;
}

void
xmms_audioscrobbler_song_free (xmms_audioscrobbler_song_t *song) {
        g_free (song->artist);
        g_free (song->track);
        g_free (song->album);
        g_free (song->mbtrackid);
}

gchar*
xmms_audioscrobbler_get_config_dir (void) {

        gchar *home_directory;
        gchar *path;

        home_directory = getenv ("HOME");
        path = (gchar*)malloc ( (strlen (home_directory) + strlen (CONFIG_DIR) + 2) * sizeof (gchar));

        g_sprintf (path, "%s/%s", home_directory, CONFIG_DIR);
      
        return path;
}

gboolean
xmms_audioscrobbler_check_config_file (gchar *filename) {

        FILE *file;
        file = fopen (filename, "r");

        if (file == NULL) {
                gchar *path = xmms_audioscrobbler_get_config_dir ();
                g_mkdir_with_parents (path, 0777); // if the file and path do not exist, create it
                file = fopen (filename, "a"); 
                if (file == NULL) return FALSE;
                else fclose (file); return TRUE; 
        } else {
                fclose (file);
                return TRUE;
        }

        return TRUE;
}

gboolean
xmms_audioscrobbler_write_config (xmms_audioscrobbler_data_t *audioscrobbler, gchar *filename) {
        
        FILE *file;
        gboolean result = FALSE;
        char buffer [256];
        int i;

        file = fopen (filename, "w");
        
        if (file == NULL) {
                printf ("\n[DEBUG] Can't open config file (%s)", filename);
                result = FALSE;
        } else { 
                sprintf (buffer, "%s:%s\n", audioscrobbler->username, audioscrobbler->password);
                i = fwrite (buffer, strlen (buffer) * sizeof (char), 1, file); 
                fclose (file);     
        }
        
        return result;
}

void 
xmms_audioscrobbler_read_config ( xmms_audioscrobbler_data_t *audioscrobbler, gchar *filename) {
        FILE *file;
        char buffer [128];
        gchar **result;
        int i;
        file = fopen (filename, "r");
        
        if (file) {
                i = fscanf (file,"%[^\n]", buffer); 
                result = g_strsplit_set (buffer, ":", -1);
         
                audioscrobbler->username = result[0];
                audioscrobbler->password = result[1];
                fclose (file);        
        } else {
                printf ("\n[DEBUG] Can't read config file (%s)", filename);        
        }       
        
}
/**
 * Build the proper post data for a audioscrobbler handshake with Last.fm
 * returns A complete handshake url
 */
gchar*
xmms_audioscrobbler_build_handshake_data (xmms_audioscrobbler_data_t *audioscrobbler) {

        gchar *url;

        gchar *auth;

        time_t timestamp = time (NULL);
        auth = xmms_audioscrobbler_build_auth_data (audioscrobbler);

        url = g_strdup_printf ("%s?hs=true&p=%s&c=%s&v=%s&u=%s&t=%ld&a=%s",
                                SCROBBLER_URL, /* char* */
                                PROTOCOL, /* char* */
                                CLIENT_ID, /* char* */
                                VERSION, /* char* */
                                audioscrobbler->username, /* char* */
                                timestamp, /* long int */
                                auth); /* char* */

        g_free (auth);
        return url;      
}       
/**
 * Do we need to handshake ?
 * returns TRUE if we need to handshake
 */
gboolean
xmms_audioscrobbler_should_handshake (xmms_audioscrobbler_data_t *audioscrobbler){
        
        if (audioscrobbler->handshake) {
                return FALSE;
        }
        
        if (time (NULL) < audioscrobbler->handshake_next) {
                printf ("\n[DEBUG] Too soon for next handshake");
                return FALSE;
        }

        if ( (audioscrobbler->username == NULL ) || (strcmp (audioscrobbler->username, "") == 0)) {
                printf ("\n[DEBUG] No username set");
                return FALSE;
        }
        
        return TRUE;
}

/**
 * Callback function to store response from curl
 */
size_t 
xmms_audioscrobbler_store_res (void *ptr, size_t size, size_t nmemb, void *userdata) {

        xmms_audioscrobbler_data_t *audioscrobbler = (xmms_audioscrobbler_data_t *) userdata;                   
        g_string_append_len (audioscrobbler->srv_response, ptr, nmemb);

        return size * nmemb;       
}

/**
 * Parse the response of the handshake and store the information properly
 */
void
xmms_audioscrobbler_parse_handshake (xmms_audioscrobbler_data_t *audioscrobbler) {

        gchar **breaks;
        int i;

        breaks = g_strsplit (audioscrobbler->srv_response->str, "\n", 4);     
        /*g_free (audioscrobbler->);*/
        audioscrobbler->status = STATUS_OK;
        audioscrobbler->status_msg = NULL;

        /* parse the result and store the informations ot its proper varaible*/
        for (i = 0; breaks[i] != NULL ; i++) {

                if (g_str_has_prefix (breaks[i], "OK")) {
                        printf ("\n[DEBUG] HANDSHAKE OK"); /* Line 1, response*/
                        if (breaks[i+1] != NULL) { /* Line 2, session ID */
                                g_free (audioscrobbler->session_id);
                                audioscrobbler->session_id = g_strdup (breaks[i+1]);

                                if (breaks[i+2] != NULL) { /* Line 3, now playing post url*/
                                        g_free (audioscrobbler->now_playing_url);
                                        audioscrobbler->now_playing_url = g_strdup (breaks[i+2]);
                                        if (breaks[i+3] != NULL) { /* Line 4, submit url */
                                                g_free (audioscrobbler->submit_url);
                                                audioscrobbler->submit_url = g_strchomp (g_strdup (breaks[i+3]));
                                        }
                                        i++;
                                }                               
                                i++;
                        }
                        i++;
                } else if ( g_str_has_prefix (breaks[i], "FAILED")) {
                        audioscrobbler->status = HANDSHAKE_FAILED;
                        printf ("\n[DEBUG] FAILED");
                        
                } else if ( g_str_has_prefix (breaks[i], "BANNED")) {
                        printf ("\n[DEBUG] BANNED");
                        audioscrobbler->status = BANNED;
                } else if ( g_str_has_prefix (breaks[i], "BADAUTH")) {
                        printf ("\n[DEBUG] BADAUTH");
                        audioscrobbler->status = BAD_PASSWORD;
                } else if ( g_str_has_prefix (breaks[i], "BADTIME")) {
                        printf ("\n[DEBUG] BADTIME");
                        audioscrobbler->status = BADTIME;
                }
        }

        g_string_free (audioscrobbler->srv_response, TRUE);   
        g_strfreev (breaks);
}

/**
 * Build authentification data which consist of md5 (md5 (password) + timestamp)
 * returns An md5 encrypted char* representing the authentification data.
 */
char* 
xmms_audioscrobbler_build_auth_data (xmms_audioscrobbler_data_t *audioscrobbler) {

        gchar *md5_password;
	gchar *md5_temp;
	gchar *md5_response;

        gchar ptimestamp[33];
        time_t timestamp;

        if (audioscrobbler->username == NULL) {
		printf ("\n[DEBUG] No username set");
		return NULL;
	}
	
	if (audioscrobbler->password == NULL) {
		printf ("\n[DEBUG] No password set");
		return NULL;
	}
		
	if (audioscrobbler->session_id == NULL) {
		printf ("\n[DEBUG] No session id");
		return NULL;
	}

        /* md5 (md5 (password) + timestamp) */
        time (&timestamp);
        snprintf ( ptimestamp, 33, "%llu", (uintmax_t)timestamp );

	md5_password = makemd5 (audioscrobbler->password);
	md5_temp = g_strconcat (md5_password,
				ptimestamp,
				NULL);
	md5_response = makemd5 (md5_temp);

	g_free (md5_password);
	g_free (md5_temp);
	
	return md5_response;
}

/**
 * Build the post data to submit the submission queue
 * returns A char* representing the post data
 */
gchar*
xmms_audioscrobbler_build_submit_data (CURL *curl, xmms_audioscrobbler_data_t *audioscrobbler) {

        g_return_val_if_fail (!g_queue_is_empty (audioscrobbler->queue), NULL);

        int i = 0;
        gchar *post_data;
    
        do {
                xmms_audioscrobbler_song_t *song;
                song = (xmms_audioscrobbler_song_t *) g_queue_pop_head (audioscrobbler->queue);
                song = xmms_audioscrobbler_escape_song (curl, song);

                post_data = g_strdup_printf ("s=%s&a[%d]=%s&t[%d]=%s&i[%d]=%s&o[%d]=%c&r[%d]=%c&l[%d]=%d&b[%d]=%s&n[%d]=%d&m[%d]=%s",
                                        audioscrobbler->session_id, /* char* */
                                        i, song->artist, /* char* */
                                        i, song->track, /* char* */
                                        i, song->timestamp, /* char* */
                                        i, song->source, /* char */
                                        i, song->rating, /* char */
                                        i, song->length, /* int */
                                        i, song->album, /* char* */
                                        i, song->tracknumber, /* int */
                                        i, song->mbtrackid); /* char* */
               
                g_queue_push_tail (audioscrobbler->submission, song);
                i++;
        } while (!g_queue_is_empty (audioscrobbler->queue));

        return post_data;                               
}

/**
 * Submit handshake,now playing or submit data to audioscrobbler protocol,
 */
void
xmms_audioscrobbler_submit_data (xmms_audioscrobbler_data_t *audioscrobbler, gint mode) {

        int status;
        CURL *curl;
        gchar *url;
        gchar curlerror[CURL_ERROR_SIZE] = "";
        gchar *post_data;
        curl = curl_easy_init ();

        if (xmms_audioscrobbler_should_handshake (audioscrobbler))
                mode = 2;

        /* mode 0 = submit, 1 = now playing, 2 = handshake */
        if (mode == 0) {
                post_data = xmms_audioscrobbler_build_submit_data (curl, audioscrobbler);
                url = audioscrobbler->submit_url;
                audioscrobbler->status = SUBMITING;
        } else if (mode == 1) {
                post_data = xmms_audioscrobbler_build_nowplaying_data (curl, audioscrobbler);
                url = audioscrobbler->now_playing_url;
        } else if (mode == 2) {
                url = xmms_audioscrobbler_build_handshake_data (audioscrobbler);
                post_data = "";
                audioscrobbler->status = HANDSHAKING;      
        } else {
                printf ("\n[DEBUG] Bad mode");
                return;        
        }

        audioscrobbler->srv_response = g_string_new (NULL);
        audioscrobbler->srv_response_size = 0;

        printf ("\n[DEBUG] URL : %s%s", url, post_data);

        curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1);
        curl_easy_setopt (curl, CURLOPT_USERAGENT, USER_AGENT);
        curl_easy_setopt (curl, CURLOPT_CONNECTTIMEOUT, 15);
        curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, xmms_audioscrobbler_store_res);
        curl_easy_setopt (curl, CURLOPT_WRITEDATA, audioscrobbler);
        curl_easy_setopt (curl, CURLOPT_URL, url);
	if (audioscrobbler->debug)
		curl_easy_setopt (curl, CURLOPT_VERBOSE, 1);

        if (mode < 2) /* use POSTFIELDS only on submission (nowplaying and submit)*/
                curl_easy_setopt (curl, CURLOPT_POSTFIELDS, post_data);

        curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, curlerror);

        status = curl_easy_perform (curl);
      
        
        curl_easy_cleanup (curl);  

        if (mode < 2) {
                g_free (post_data); // only free if it's used
                xmms_audioscrobbler_parse_submit (audioscrobbler);
        } else if (mode == 2) {      
                audioscrobbler->handshake_next = time (NULL) + 1800;
                printf ("\n[INFO] Performing handshake with Audioscrobbler server");
                xmms_audioscrobbler_parse_handshake (audioscrobbler);
        }   

}

/**
 * Build post data for nowplaying submission
 * returns A char* containing the post fields for now playing submit
 */
gchar*
xmms_audioscrobbler_build_nowplaying_data (CURL *curl, xmms_audioscrobbler_data_t *audioscrobbler) {
        
        g_return_val_if_fail (!g_queue_is_empty (audioscrobbler->queue), NULL);
        
        xmms_audioscrobbler_song_t *song;
        gchar *post_data;        

        song = (xmms_audioscrobbler_song_t *) g_queue_pop_tail (audioscrobbler->queue); 
        song = xmms_audioscrobbler_escape_song (curl, song);
        
        post_data = g_strdup_printf ("s=%s&a=%s&t=%s&b=%s&l=%d&n=%d&m=%s",
                                audioscrobbler->session_id, /* char* */
                                song->artist, /* char* */
                                song->track, /* char* */
                                song->album, /* char* */
                                song->length, /* int */
                                song->tracknumber, /* int */
                                song->mbtrackid); /* char* */

        g_queue_push_tail (audioscrobbler->queue, song);

        return post_data;    
}       

/**
 * Parse the submit of NowPlaying and standard submission
 */
void
xmms_audioscrobbler_parse_submit (xmms_audioscrobbler_data_t *audioscrobbler) {

        g_free (audioscrobbler->status_msg);
        audioscrobbler->status = STATUS_OK;
        audioscrobbler->status_msg = NULL;
  
        if (g_str_has_prefix (audioscrobbler->srv_response->str, "OK")) {
                printf ("\n[DEBUG] OK");
                audioscrobbler->status = STATUS_OK;
        } else if ( g_str_has_prefix (audioscrobbler->srv_response->str, "BADSESSION")) {
                printf ("\n[DEBUG] BADSESSION");  
                audioscrobbler->status = BADSESSION;   
        } else if ( g_str_has_prefix (audioscrobbler->srv_response->str, "FAILED")) {
                printf ("\n[DEBUG] FAILED"); 
                audioscrobbler->status_msg= g_strdup (audioscrobbler->srv_response->str);
                audioscrobbler->status = SUBMIT_FAILED;                 
        }

        g_string_free (audioscrobbler->srv_response, TRUE);
}  

/**
 * Usefull function which HTML espace need string
 * returns A xmms_audioscrobbler_song_t escaped
 */
xmms_audioscrobbler_song_t *
xmms_audioscrobbler_escape_song (CURL *curl, xmms_audioscrobbler_song_t *song) {

        xmms_audioscrobbler_song_t *escaped;
        escaped = g_new (xmms_audioscrobbler_song_t, 1);

        //escaped->artist = curl_easy_escape (curl, song->artist, strlen (song->artist));
        //escaped->track = curl_easy_escape (curl, song->track, strlen (song->track));
        escaped->artist = lastfm_parse (song->artist);
        escaped->track = lastfm_parse (song->track);
        escaped->source = song->source;
        escaped->rating = song->rating;
        escaped->length = song->length;
        escaped->album = curl_easy_escape (curl, ifnull (song->album), strlen (song->album));
        escaped->tracknumber = song->tracknumber;
        escaped->mbtrackid = curl_easy_escape (curl, ifnull (song->mbtrackid), strlen (song->mbtrackid));

        snprintf (escaped->timestamp, 33, "%s", song->timestamp);

        xmms_audioscrobbler_song_free (song);

        return escaped;
}

/**
 * Utility function to encrypt a string with md5
 */
static gchar *
makemd5 (char *string) {
	md5_state_t md5state;
	guchar md5pword[16];
	gchar md5_response[33];

	int j = 0;

	memset (md5_response, 0, sizeof (md5_response));

	md5_init (&md5state);
	md5_append (&md5state, (unsigned char*)string, strlen (string));
	md5_finish (&md5state, md5pword);

	for (j = 0; j < 16; j++) {
		char a[3];
		sprintf (a, "%02x", md5pword[j]);
		md5_response[2*j] = a[0];
		md5_response[2*j+1] = a[1];
	}

	return (g_strdup (md5_response));
}

/* If the string is null, replace it with "", usefull to HTML escape*/
static gchar* 
ifnull (gchar *s) {
        return s == NULL ? g_strdup ("") : s;       
}               

/* Is the song submitable ? ( duration > 30 seconds)*/
static gboolean
song_is_submitable (gint length) {
        return length > 30;
}

/**
 * Calculate when we should submit the song, which is: after 240 seconds 
 * OR half the song length if the total duration is less than 240 seconds 
 */
static gint 
calculate_submit_time (xmms_audioscrobbler_song_t *song) {

        if (song->length < 240 && song->length > 30) 
                return (song->length / 2);
        else
                return 240;
            
}

static gchar* lastfm_parse (gchar *s) {
        gint i = 0;
        
        while ( s[i] != '\0') {
                if (s[i] == ' ')
                        s[i] = '+';
                i++;
        }

        return g_strdup (s);
}
